package cn.backflow.admin.controller;

import cn.backflow.admin.Configuration;
import cn.backflow.admin.entity.FileMapping;
import cn.backflow.admin.entity.User;
import cn.backflow.admin.service.FileMappingService;
import cn.backflow.data.pagination.PageRequest;
import cn.backflow.secure.annotation.Authorization;
import cn.backflow.utils.Files;
import cn.backflow.utils.JsonMap;
import cn.backflow.utils.Qiniu;
import cn.backflow.utils.Strings;
import cn.backflow.web.BaseSpringController;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.util.StringMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

import static cn.backflow.admin.controller.IndexController.getCurrentUser;

@RestController
@RequestMapping("filemapping")
public class FileMappingController extends BaseSpringController {

    private static final Logger LOG = LoggerFactory.getLogger(FileMappingController.class);

    private static final String DEFAULT_SORT_COLUMNS = "folder desc"; // 默认文件夹在最前

    @Resource
    private FileMappingService fileMappingService;

    public static FileMapping fromFileAndQiniuResponse(MultipartFile file, Response res, Date time) throws QiniuException {
        FileMapping mapping = new FileMapping();
        if (res.isJson()) {
            StringMap map = res.jsonToMap();
            mapping.setKey(map.get("key").toString());
            mapping.setHash(map.get("hash").toString());
        }
        mapping.setUrl(Qiniu.buildUrl(mapping.getKey()));
        mapping.setSize(file.getSize());
        mapping.setName(file.getOriginalFilename());
        mapping.setExt(Strings.extension(mapping.getName()));
        mapping.setMime(file.getContentType());
        mapping.setUploaded(time);
        mapping.setUpdated(time);
        return mapping;
    }

    /* 列表 */
    @RequestMapping
    public Object index(HttpServletRequest request) {
        PageRequest pr = pageRequest(request, DEFAULT_SORT_COLUMNS);

        // 默认查询根目录下的文件
        if (!pr.getFilters().containsKey("parent")) {
            pr.addFilter("parent", 0);
        }
        String sort = pr.getFilter("sort");
        String asc = pr.getFilter("asc");
        pr.setSortColumns(DEFAULT_SORT_COLUMNS + ", " + sort + ("true".equals(asc) ? "" : " desc"));

        return fileMappingService.findPage(pr);
    }

    @RequestMapping("{id}")
    @Authorization("file.view")
    public Object get(@PathVariable Integer id) {
        return fileMappingService.getById(id);
    }

    /* 获取指定ID文件的所有父文件(夹),包含该文件(夹)本身 */
    @RequestMapping("parents")
    public Object parents(@RequestParam("parent") Integer parent) {
        List<FileMapping> mappings = fileMappingService.findParentsById(parent);
        return mappings.stream().map(m -> {
            HashMap<String, Object> map = new HashMap<>(2);
            map.put("name", m.getName());
            map.put("id", m.getId());
            return map;
        }).collect(Collectors.toList());
    }

    // 上传文件, 文件名为 file
    @RequestMapping("upload")
    @Authorization("file.upload")
    public Object upload(MultipartHttpServletRequest request) {
        JsonMap json = JsonMap.succeed();
        MultipartFile file = request.getFile("file");
        FileMapping mapping;
        try {
            Response res = Qiniu.upload(file.getInputStream(), file.getOriginalFilename());
            LOG.info(res.bodyString());
            if (!res.isOK()) {
                return json.success(false).msg(file.getOriginalFilename() + " 上传失败!");
            }
            mapping = fromFileAndQiniuResponse(file, res, new Date());
        } catch (Exception e) {
            e.printStackTrace();
            return json.success(false).msg(e.getMessage());
        }
        fileMappingService.save(mapping);
        return json.put("file", mapping);
    }

    @RequestMapping("uptoken")
    public Object uptoken() {
        return JsonMap.succeed().put("uptoken", Qiniu.uptoken());
    }

    // 批量上传 (一次多个文件, 文件名为 files)
    @RequestMapping("uploads")
    @Authorization("file.upload")
    public Object uploads(MultipartHttpServletRequest request) {
        JsonMap json = JsonMap.succeed();

        List<MultipartFile> filelist = request.getFiles("files");
        List<FileMapping> mappings = new ArrayList<>();
        List<String> failed = new ArrayList<>();
        Date now = new Date();
        try {
            for (MultipartFile file : filelist) {
                Response res = Qiniu.upload(file.getBytes(), file.getOriginalFilename());
                LOG.info(res.toString());
                if (!res.isOK()) {
                    failed.add(file.getOriginalFilename());
                    continue;
                }
                mappings.add(fromFileAndQiniuResponse(file, res, now));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return json.success(false).msg(e.getMessage());
        }
        fileMappingService.save(mappings);
        if (!failed.isEmpty()) {
            json.put("failed", failed);
        }
        return json.put("files", mappings);
    }

    /* 保存或重命名文件 */
    @Authorization("file.upload")
    @RequestMapping(method = RequestMethod.POST)
    public Object save(@Valid FileMapping mapping, BindingResult errors, HttpServletRequest request) {
        User user = getCurrentUser(request);
        JsonMap json = JsonMap.succeed();
        if (errors.hasErrors()) {
            return filedErrors(errors, json);
        }
        if (mapping.getId() != null && mapping.getId() <= Configuration.SYSTEM_RESERVE_FILEMAPPINGS && !user.isSuperAdmin()) {
            return json.success(false).msg("不能修改系统保留文件(夹).");
        }
        // 创建文件时, 补充缺失的属性
        if (mapping.getFolder() == 0) {
            if (mapping.getUrl() == null) {
                if (mapping.getKey() == null) {
                    return json.success(false).msg("文件地址(url)不能为空!");
                }
                mapping.setUrl(Qiniu.buildUrl(mapping.getKey()));
            }
            if (mapping.getExt() == null) {
                mapping.setExt(Strings.extension(mapping.getName()));
            }
        }
        Date now = new Date();
        mapping.setCreator(user.getId());
        mapping.setUploaded(now);
        mapping.setUpdated(now);
        fileMappingService.saveOrUpdate(mapping);
        return json.put("file", mapping);
    }

    /* 更新文件 */
    @Authorization("file.edit")
    @RequestMapping(method = RequestMethod.PUT)
    public Object update(@Valid FileMapping mapping, BindingResult errors) {
        JsonMap json = JsonMap.succeed();
        if (mapping.getId() <= Configuration.SYSTEM_RESERVE_FILEMAPPINGS) {
            return json.success(false).msg("不能删除系统保留文件(夹).");
        }
        if (errors.hasErrors()) {
            return filedErrors(errors, json);
        }
        String oldKey = mapping.getUrl().replaceFirst(Qiniu.QINIU_DOMAIN, "");
        mapping.setExt(Strings.extension(mapping.getName()));
        mapping.setUrl(Qiniu.buildUrl(mapping.getKey()));
        fileMappingService.update(mapping);
        Qiniu.delete(oldKey);
        return json.put("file", mapping);
    }

    /* 删除 */
    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    public Object delete(@PathVariable Integer id) {
        if (id <= Configuration.SYSTEM_RESERVE_FILEMAPPINGS) {
            return JsonMap.fail("不能删除系统保留文件(夹).");
        }
        fileMappingService.deleteById(id);
        return JsonMap.succeed();
    }

    @RequestMapping("download")
    public void download(Integer[] ids, HttpServletResponse response) {
        List<FileMapping> mappings = fileMappingService.findByIds(Arrays.asList(ids));
        if (mappings.isEmpty()) return;
        Map<String, String> map = new HashMap<>(mappings.size());
        mappings.forEach(m -> {
            int i = 0;
            String name = m.getName();
            while (map.containsKey(name)) { // rename duplicate filenames
                String[] parts = Strings.filenameParts(m.getName());
                name = parts[1] + "_" + ++i + (parts[2].isEmpty() ? parts[2] : "." + parts[2]);
            }
            map.put(name, m.getUrl());
        });
        String filename = mappings.get(mappings.size() - 1).getName() + ".zip";
        try {
            ByteArrayOutputStream zos = Files.getZipOutputStream(map);
            zos.close();
            response.setContentType("application/force-download");// 设置强制下载不打开
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filename, "utf-8")); // 设置文件名
            response.getOutputStream().write(zos.toByteArray());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /* 批量删除 */
    @RequestMapping(method = RequestMethod.DELETE)
    public Object delete(@RequestParam("items") Set<Integer> items) {
        JsonMap json = JsonMap.succeed();
        boolean illegal = items.removeIf(i -> i <= Configuration.SYSTEM_RESERVE_FILEMAPPINGS);
        if (illegal) {
            json.msg("不能删除系统保留文件(夹).");
        }
        int effect = fileMappingService.deleteBatch(items);
        return json.success(effect > 0);
    }

    /* 按key批量删除 */
    @RequestMapping(value = "keys", method = RequestMethod.DELETE)
    public Object delete(@RequestParam("items") String[] items) {
        fileMappingService.deleteByKeys(items);
        return JsonMap.succeed();
    }
}